package org.judo.controller;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.judo.admin.identity.AppUser;
import org.judo.components.Displayable;
import org.judo.components.HTMLControl;
import org.judo.components.JudoComponent;
import org.judo.components.state.GroupScope;
import org.judo.components.state.ComponentPersistence;
import org.judo.database.orm.mappers.LazyInitList;
import org.judo.propertyobject.PropertyNotFoundException;
import org.judo.propertyobject.PropertyObject;
import org.judo.util.Util;

public class PageController extends JudoComponent 
	implements PageControllerLifecycle
{
	public static final String NO_FORWARD_URL = "__NO_FORWARD";
	private String forwardURL = NO_FORWARD_URL;
	
	protected HttpServletRequest request;
	protected HttpServletResponse response;

	public AppUser userObject()
	{
		return (AppUser)request.getSession().getAttribute("__USER");
	}
	
	public boolean isLoggedIn()
	{
		if (request.getSession().getAttribute("__USER") != null)
			return true;
		
		return false;
	}
	
	public void populateRequest(HttpServletRequest request)
	{
		this.request = request;
	}
	
	public void populateResponce(HttpServletResponse response)
	{
		this.response = response;
	}
	
	//TODO: finish this method
	protected PageController getController(String relativeUrl)
	{
		PageController controller = null;
		
		return controller;
	}
	
	public PageController getControllerFromGroup(Class controllerClass)
		throws ControllerNotFoundException
	{
		PageController page = null;
		
		String name = controllerClass.getCanonicalName();
		
		ComponentPersistence cp = 
			ComponentPersistence.getInstance(request.getSession());
		
		GroupScope targetGroupAnnotation = (GroupScope)
			controllerClass.getAnnotation(GroupScope.class);
		
		GroupScope thisGroupAnnotation = (GroupScope)
			this.getClass().getAnnotation(GroupScope.class);
	
		if (thisGroupAnnotation == null)
		{
			throw new ControllerNotFoundException("The controller '"+controllerClass.getCanonicalName()+"' could not be located because '"+this.getClass().getCanonicalName()+"' is not in a group as defined by annotating with @ComponentGroup(\"<group-name>\")");
		}
		
		if (targetGroupAnnotation != null)
		{
			String thisGroupName = null;
			String targetGroupName = null;
			
			try
			{
				thisGroupName = thisGroupAnnotation.value();
				targetGroupName = targetGroupAnnotation.value();
				
				if (targetGroupName.equals(thisGroupName))
				{
					page = (PageController)cp.getGroupComponent(
							controllerClass,targetGroupAnnotation.value(),connections);
					
					long transactionId = this.localTransactionId();
					page.populateTransactionId(transactionId);
					
					loadedComponents.add(page);
				}
				else
					throw new ControllerNotFoundException("The controller '"+controllerClass.getCanonicalName()+"' (Group: '"+targetGroupName+"') is not in the same group as '"+this.getClass().getCanonicalName()+"' (Group: '"+thisGroupName+"')");
			}
			catch (Exception e)
			{
				throw new ControllerNotFoundException("The controller '"+controllerClass.getCanonicalName()+"' could not be located because: "+e);
			}
		}
		else
			throw new ControllerNotFoundException("The controller '"+controllerClass.getCanonicalName()+"' does not have 'Group' lifecycle scope as specified by annotating with @ComponentGroup(\"<group-name>\")");
		
		return page;
	}
	
	public String showPage()
	{
		return forwardURL;
	}

	public void showPage(String forwardURL)
	{
		this.forwardURL = forwardURL;
	}
	
	public void showHomePage()
	{
		this.forwardURL = "/app_home/home.do";
	}
	
	public void writePropsToRequest()
	{
		SimpleDateFormat dateFormater = new SimpleDateFormat("MM/dd/yyyy");
		Iterator props = this.keySet().iterator();
		
		while (props.hasNext())
		{
			String prop = (String)props.next();
			
			Object value = null;
			Object rawValue = null;
						
			value = this.get(prop);
			rawValue = this.getRawPropertyValue(prop);
			
			if (rawValue != null && rawValue instanceof HTMLControl)
			{
				HTMLControl control = (HTMLControl)rawValue;
				control.setName(prop);
				request.setAttribute("html_"+prop, control.generateHTML());
			}
			
			if (rawValue != null && rawValue instanceof Displayable)
			{
				Displayable control = (Displayable)rawValue;
				request.setAttribute("display_"+prop, control.displayValue());
			}
			
			if (!this.propertyIsService(prop))
			{
				if (value instanceof Collection)
				{
					List list = checkList(value); 
					request.setAttribute(prop,list);
				}
				else if (value instanceof java.util.Date)
				{
					String parsedDate = dateFormater.format((Date)value);
					request.setAttribute(prop,parsedDate);
				}
				else
					request.setAttribute(prop,value);
			}
		}
	}
	
	protected List checkList(Object value)
	{
		if (value instanceof LazyInitList)
			((LazyInitList)value).startIgnoreInit();	
		
		ArrayList newList = new ArrayList();
		Iterator list = ((Collection)value).iterator();
			
		while (list.hasNext())
		{
			Object cur = list.next();
			
			if (cur instanceof PropertyObject)
			{
				PropertyObject propObj = (PropertyObject)cur;
				HashMap map = new HashMap();
				Iterator curProps = propObj.keySet().iterator();
				
				while (curProps.hasNext())
				{
					String name = (String)curProps.next();
					map.put(name,propObj.get(name));
				}
				
				newList.add(map);
			}
			else
				newList.add(cur);
		}
		
		if (value instanceof LazyInitList)
			((LazyInitList)value).endIgnoreInit();
		
		return newList;
	}
	
	/*
	 * ************************************************
	 * *******************CRUD METHODS*****************
	 * ************************************************
	 */
	
	//TODO: finish these methods
	
	/**
	 * Used when the @AutoUpdate annotation is applied
	 * to class 
	 */
	public void update()
		throws Exception
	{	
	}
	
	/**
	 * 
	 */
	public void find()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void findWithCriteial()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void insert()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void delete()
		throws Exception
	{	
	}
	
	/**
	 * 
	 */
	public void query()
		throws Exception 
	{	
	}
	
	/*
	 * ************************************************
	 * *************AUTO CRUD METHOD CALLBACKS*********
	 * ************************************************
	 */
	
	/**
	 * 
	 */
	public void beforeUpdate()
		throws Exception
	{	
	}
	
	/**
	 * 
	 */
	public void afterUpdate()
		throws Exception
	{	
	}
	
	/**
	 * 
	 */
	public void beforeFind()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void afterFind()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void beforeFindWithCriteial()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void afterFindWithCriteial()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void beforeInsert()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void afterInsert()
		throws Exception
	{
	}
	
	/**
	 * 
	 */
	public void beforeDelete()
		throws Exception
	{	
	}
	
	/**
	 * 
	 */
	public void afterDelete()
		throws Exception
	{	
	}
	
	/**
	 * 
	 */
	public void beforeQuery()
		throws Exception
	{	
	}
	
	/**
	 * 
	 */
	public void afterQuery()
		throws Exception
	{	
	}
	
	/*
	 * ************************************************
	 * ***************LIFECYCLE METHODS****************
	 * ************************************************
	 */
	
	public void beforeRequestAction()
	{	
	}
	
	public void afterRequestAction()
	{
	}

	public void sessionScopeBegins()
	{	
	}

	public void sessionScopeEnds()
	{	
	}

	public void onError(Throwable e)
	{
	}
}
